package blms.system;

import java.io.File;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.List;

import blms.system.exception.LeagueDoesNotExistException;
import blms.system.exception.UnknownAttribute;
import blms.system.exception.UserDoesNotExistException;
import blms.system.exception.restrictions.RestrictionException;
import blms.system.league.League;
import blms.system.league.LeagueEntry;
import blms.system.manager.LeaguesManager;
import blms.system.manager.MatchesManager;
import blms.system.user.User;
import blms.system.user.UserEntry;
import blms.system.util.BLMSMessages;
import blms.system.util.Validator;

/**
 *
 */
public class BLMS {
	
	private LeaguesManager leagueManager;
	private final UserEntry userEntry;
	private final LeagueEntry leagueEntry;
	private MatchesManager matchesManager;
	private SimpleDateFormat dateFormat;
	
	public BLMS() {
		
		userEntry = new UserEntry();
		leagueEntry = new LeagueEntry(userEntry);
		leagueManager = new LeaguesManager();
		matchesManager = new MatchesManager();
	}

	/**
	 * Creates user for the system.
	 * 
	 * @param firstName The first name of the user.
	 * @param lastName The last name of the user.
	 * @param homePhone The home phone of the user.
	 * @param workPhone The work phone of the user.
	 * @param cellPhone The cell phone of the user.
	 * @param email The email of the user. This attribute will uniquely identify the user.
	 * @param picture A picture of the user.
	 * @return The ID of the user created.
	 * @throws RestrictionException
	 */
	public String createUser(String firstName, String lastName, String homePhone, String workPhone, String cellPhone,
								String email, File picture) throws RestrictionException {
		
		return userEntry.createUser(firstName, lastName, homePhone, workPhone, cellPhone, email, picture);
	}

	/**
	 * Returns the value of the required attribute from the user represented by the id.
	 * 
	 * @param id The id of the user.
	 * @param attribute The attribute to be returned.
	 * @return the value of the required attribute from the user represented by the id.
	 * @throws UserDoesNotExistException
	 * @throws UnknownAttribute
	 */
	public String getUserAttribute(String id, String attribute) throws UserDoesNotExistException, UnknownAttribute {
		
		try {
			return userEntry.getUserAttribute(id, attribute);
		} catch (UnknownAttribute e) {
			throw new UnknownAttribute(BLMSMessages.UNKNOWN_USER_ATTRIBUTE_MESSAGE);
		}
	}

	/**
	 * 
	 * Changes the given attribute of the specified user to the specified value.
	 * @param id The id of the user.
	 * @param attribute The attribute to be replaced.
	 * @param value The new value of the attribute.
	 * @throws UserDoesNotExistException If the user does not exist.
	 * @throws UnknownAttribute If there is no attribute with the specified name.
	 * @throws RestrictionException
	 */
	public void changeUserAttribute(String id, String attribute, String value) throws UserDoesNotExistException, 
																						UnknownAttribute, RestrictionException {
		
		try {
			userEntry.changeUserAttribute(id, attribute, value);
		} catch (UnknownAttribute e) {
			throw new UnknownAttribute(BLMSMessages.UNKNOWN_USER_ATTRIBUTE_MESSAGE);
		}
	}

	/**
	 * Finds the users with the specified last name.
	 * 
	 * @param lastName The last name of the users to be found.
	 * @return The users with the specified last name.
	 */

	public List<String> findUserByLastName(String lastName) throws UserDoesNotExistException {
		return userEntry.findUserByLastName(lastName);
	}

	/**
	 * Removes the specified user from the system.
	 * 
	 * @param id The id of the user to be removed.
	 * @throws RestrictionException 
	 * @throws UserDoesNotExistException 
	 */
	public void deleteUser(String id) throws RestrictionException, UserDoesNotExistException {
	
		User user = userEntry.getUser(id);
		
		for (League league : leagueEntry.getLeagues()) {
			if(league.getOperator().equals(user)) {
				throw new RestrictionException(BLMSMessages.CANNOT_REMOVE_LEAGUE_OPERATOR_MESSAGE);
			}
		}
		
		userEntry.remove(id);
	}

	/**
	 * Removes all users from the system.
	 */
	public void removeAllUsers() {
		userEntry.removeAllUsers();
	}
	
	/**
	 * Creates a new league
	 * 
	 * @param name The league name
	 * @param operator The operator email
	 * @param creationDate TODO
	 * @return an identifier to the league
	 * @throws RestrictionException 
	 * @throws UserDoesNotExistException 
	 */
	public String createLeague(String name, String operator, Date creationDate) throws RestrictionException, UserDoesNotExistException{

		if (operator == null || operator.equals("")) {
			throw new RestrictionException(BLMSMessages.REQUIRED_DATA_MESSAGE+"league operator");
		}
		
		User userOperator = userEntry.getUser(operator);
		
		League league = leagueEntry.createLeague(name, userOperator, creationDate);
		leagueManager.join(league, userOperator);
		
		return league.getId();
	}

	/**
	 * Returns the value of the required attribute from the league represented by the id.
	 * 
	 * @param leagueID The id of the league.
	 * @param attribute The attribute to be returned.
	 * @return the value of the required attribute from the user represented by the id.
	 * @throws LeagueDoesNotExistException 
	 * @throws UnknownAttribute 
	 */
	public String getLeagueAttribute(String leagueID, String attribute) throws LeagueDoesNotExistException, UnknownAttribute {
		return leagueEntry.getLeagueAttribute(leagueID, attribute);
	}
	
	/**
	 * @param leagueID
	 * @param attribute
	 * @param value
	 * @throws RestrictionException
	 * @throws LeagueDoesNotExistException
	 * @throws UnknownAttribute
	 * @throws UserDoesNotExistException
	 */
	public void changeLeagueAttribute(String leagueID, String attribute, String value) throws RestrictionException,
																						LeagueDoesNotExistException,
																							UnknownAttribute, 
																								UserDoesNotExistException {
		leagueEntry.changueLeagueAttributed(leagueID, attribute, value);
	}

	/**
	 * Finds the league with the specified name.
	 * 
	 * @param name The name of the league to be found.
	 * @return The league with the specified name.
	 */
	public List<String> findLeague(String name) throws LeagueDoesNotExistException {
		return leagueEntry.findLeague(name);
	}

	/**
	 * Removes the specified user from the system.
	 * 
	 * @param leagueID The id of the user to be removed.
	 * @throws RestrictionException 
	 */
	public void deleteLeague(String leagueID) {
		leagueEntry.deleteLeague(leagueID);
	}

	/**
	 * 
	 * Verifies whether the specified user is member of the specified league.
	 * 
	 * @param userID The id of the user.
	 * @param leagueID The id of the league.
	 * @return true if the user is a member of the league; false otherwise.
	 * @throws LeagueDoesNotExistException 
	 * @throws UserDoesNotExistException 
	 * @throws RestrictionException 
	 */
	public boolean isLeagueMember(String userID, String leagueID) throws RestrictionException, LeagueDoesNotExistException, 
																			UserDoesNotExistException {
		
		try {
			return leagueManager.isLeagueMember(userEntry.getUser(userID), leagueEntry.getLeague(leagueID));
		} catch (NumberFormatException e) {
			throw new RestrictionException(BLMSMessages.UNKNOWN_LEAGUE_MESSAGE);
		}
	}

	/**
	 * Returns the leagues that the specified user is a member.
	 * 
	 * @param id The id of the user.
	 * @return the leagues that the specified user is a member.
	 * @throws UserDoesNotExistException
	 */
	public Collection<League> getPlayerLeagues(String id) throws UserDoesNotExistException {
		return leagueManager.getPlayerLeagues(userEntry.getUser(id));
	}

	/**
	 * Returns the members of the specified league.
	 * @param leagueID The id of the league.
	 * @return the members of the specified league.
	 * @throws LeagueDoesNotExistException
	 */
	public Collection<User> getLeagueMembers(String leagueID) throws LeagueDoesNotExistException {
		return leagueManager.getLeagueMembers(leagueEntry.getLeague(leagueID));
	}

	/**
	 * Binds a specified user to a league.
	 * 
	 * @param userID
	 * @param leagueID
	 * @param initialHandcap
	 * @throws UserDoesNotExistException 
	 * @throws LeagueDoesNotExistException 
	 * @throws RestrictionException 
	 */
	public void joinLeague(String userID, String leagueID, String initialHandicap) throws UserDoesNotExistException,
																							LeagueDoesNotExistException,
																								RestrictionException {

		if (Validator.isBlankOrNull(initialHandicap)) throw new RestrictionException(BLMSMessages.MUST_PROVIDE_HANDICAP_MESSAGE);
		leagueManager.join(leagueEntry.getLeague(leagueID), userEntry.getUser(userID), Integer.parseInt(initialHandicap));
	}

	/**
	 * Removes all leagues from BMLS system.
	 */
	public void removeAllLeagues() {
		leagueEntry.removeAllLeagues();
		leagueManager = new LeaguesManager();
	}
	
	/**
	 * @param userId
	 * @param leagueID
	 * @param attribute
	 * @return
	 * @throws LeagueDoesNotExistException
	 * @throws RestrictionException
	 * @throws UnknownAttribute
	 * @throws UserDoesNotExistException 
	 */
	public Object getUserLeagueAttribute(String userID, String leagueID, String attribute) throws RestrictionException,
																									LeagueDoesNotExistException, 
																										UnknownAttribute, 
																											UserDoesNotExistException {
		try {
			return leagueManager.getUserLeagueAttribute(userEntry.getUser(userID), leagueEntry.getLeague(leagueID), attribute);
		} catch (UnknownAttribute e) {
			throw new UnknownAttribute(BLMSMessages.UNKNOWN_USER_ATTRIBUTE_MESSAGE);
		}
	}

	/**
	 * 
	 * @param userID
	 * @param leagueID
	 * @throws UserDoesNotExistException
	 * @throws LeagueDoesNotExistException
	 * @throws RestrictionException
	 */
	public void leaveLeague(String userID, String leagueID) throws LeagueDoesNotExistException, UserDoesNotExistException,
																	RestrictionException {
		
		leagueManager.leaveLeague(userEntry.getUser(userID), leagueEntry.getLeague(leagueID));
	}
	
	/**
	 * System facade for <code>MatchesManager.getNumberOfMatches</code>
	 * @throws RestrictionException 
	 * 
	 * @see MatchesManager.getNumberOfMatches
	 */
	public int getNumberOfMatches(String leagueID) throws RestrictionException {
		
		if(!leagueEntry.containsLeagueByID(leagueID)) throw new RestrictionException("Unknown league");
		return matchesManager.getNumberOfMatches(leagueID);
	}
	
	/**
	 * System facade for <code>MatchesManager.getNumberOfMatches</code>
	 * @throws RestrictionException 
	 * 
	 * @see MatchesManager.getNumberOfMatches
	 */
	public int getNumberOfMatches(String playerID, String leagueID) throws RestrictionException {
		
		if(!leagueEntry.containsLeagueByID(leagueID)) throw new RestrictionException("Unknown league");
		if(!userEntry.containsUser(playerID)) throw new RestrictionException("Unknown user");
		return matchesManager.getNumberOfMatches(playerID, leagueID);
	}
	
	/**
	 * System facade for <code>MatchesManager.addMatchResult</code>
	 * @throws RestrictionException 
	 * 
	 * @see MatchesManager.addMatchResult
	 */
	public int addMatchResult(String leagueID, Date date, String winnerID, String loserID) throws RestrictionException {
		
		if(!leagueEntry.containsLeagueByID(leagueID)) throw new RestrictionException("Unknown league");
		if(!userEntry.containsUser(winnerID)) throw new RestrictionException("Unknown user");
		if(!userEntry.containsUser(loserID)) throw new RestrictionException("Unknown user");
		
		return matchesManager.addMatchResult(leagueID, date, winnerID, loserID);
	}
	
	/**
	 * System facade for <code>MatchesManager.addMatchResult</code>
	 * @throws RestrictionException 
	 * 
	 * @see MatchesManager.addMatchResult
	 */
	public int addMatchResult(String leagueID, Date date, String winnerID, String loserID, String length,
								String score, String longestRunForWinner, String longestRunForLoser) throws RestrictionException {
		
		if(!leagueEntry.containsLeagueByID(leagueID)) throw new RestrictionException("Unknown league");
		if(!userEntry.containsUser(winnerID)) throw new RestrictionException("Unknown user");
		if(!userEntry.containsUser(loserID)) throw new RestrictionException("Unknown user");
		
		int parsedLength = Integer.MIN_VALUE;
		int parsedScore = Integer.MIN_VALUE;
		int parsedLongestRunForWinner = Integer.MIN_VALUE;
		int parsedLongestRunForLoser = Integer.MIN_VALUE;
		
		try {
			parsedLength = Integer.parseInt(length);
		}catch (NumberFormatException e) {
			throw new RestrictionException("Invalid match length");
		}
		
		try {
			parsedScore = Integer.parseInt(score);
		}catch (NumberFormatException e) {
			throw new RestrictionException("Invalid score");
		}
		
		try {
			parsedLongestRunForWinner = Integer.parseInt(longestRunForWinner);
		}catch (NumberFormatException e) {
			throw new RestrictionException("Invalid run");
		}
		
		try {
			parsedLongestRunForLoser = Integer.parseInt(longestRunForLoser);
		}catch (NumberFormatException e) {
			throw new RestrictionException("Invalid run");
		}
		
		
		return matchesManager.addMatchResult(leagueID, date, winnerID, loserID, parsedLength, parsedScore,
												parsedLongestRunForWinner, parsedLongestRunForLoser);
	}
	

	/**
	 * System facade for <code>MatchesManager.addMatchResult</code>
	 * @throws RestrictionException 
	 * 
	 * @see MatchesManager.addMatchResult
	 */
	public void updateMatchResult(int matchID, Date date, String winnerID, String loserID, int length,
									int score, int longestRunForWinner,	int longestRunForLoser) throws RestrictionException {
		
		if(!userEntry.containsUser(winnerID)) throw new RestrictionException("Unknown user");
		if(!userEntry.containsUser(loserID)) throw new RestrictionException("Unknown user");
		matchesManager.updateMatchResult(matchID, date, winnerID, loserID, length, score, longestRunForWinner, longestRunForLoser);
	}
	
	/**
	 * System facade for <code>MatchesManager.getNumberOfWins</code>
	 * @throws RestrictionException 
	 * 
	 * @see MatchesManager.getNumberOfWins
	 */
	public int getNumberOfWins(String playerID, String leagueID) throws RestrictionException {
		
		if(!leagueEntry.containsLeagueByID(leagueID)) throw new RestrictionException("Unknown league");
		if(!userEntry.containsUser(playerID)) throw new RestrictionException("Unknown user");
		return matchesManager.getNumberOfWins(playerID, leagueID);
	}
	
	/**
	 * System facade for <code>MatchesManager.getNumberOfLosses</code>
	 * @throws RestrictionException 
	 * 
	 * @see MatchesManager.getNumberOfLosses
	 */
	public int getNumberOfLosses(String playerID, String leagueID) throws RestrictionException {
		
		if(!leagueEntry.containsLeagueByID(leagueID)) throw new RestrictionException("Unknown league");
		if(!userEntry.containsUser(playerID)) throw new RestrictionException("Unknown user");
		return matchesManager.getNumberOfLosses(playerID, leagueID);
	}
	
	/**
	 * System facade for <code>MatchesManager.getMatch</code>
	 * @throws RestrictionException 
	 * 
	 * @see MatchesManager.getMatch
	 */
	public int getMatch(String leagueID, int index) throws RestrictionException {
		
		if(!leagueEntry.containsLeagueByID(leagueID)) throw new RestrictionException("Unknown league");
		return matchesManager.getMatchID(leagueID, index);
	}
	
	/**
	 * System facade for <code>MatchesManager.getMatch</code>
	 * @throws RestrictionException 
	 * 
	 * @see MatchesManager.getMatch
	 */
	public int getMatch(String playerID, String leagueID, int index) throws RestrictionException {
		
		if(!leagueEntry.containsLeagueByID(leagueID)) throw new RestrictionException("Unknown league");
		if(!userEntry.containsUser(playerID)) throw new RestrictionException("Unknown user");
		return matchesManager.getMatch(playerID, leagueID, index);
	}
	
	/**
	 * System facade for <code>MatchesManager.getMatchByDate</code>
	 * @throws RestrictionException 
	 * 
	 * @see MatchesManager.getMatchByDate
	 */
	public int getMatchByDate(String leagueID, Date startDate, Date endDate, int index) throws RestrictionException {
		
		if(!leagueEntry.containsLeagueByID(leagueID)) throw new RestrictionException("Unknown league");
		return matchesManager.getMatchByDate(leagueID, startDate, endDate, index);
	}
	
	/**
	 * System facade for <code>MatchesManager.getMatchByDate</code>
	 * @throws RestrictionException 
	 * 
	 * @see MatchesManager.getMatchByDate
	 */
	public int getMatchByDate(String playerID, String leagueID, Date startDate, Date endDate, int index) throws RestrictionException {
		
		if(!leagueEntry.containsLeagueByID(leagueID)) throw new RestrictionException("Unknown league");
		if(!userEntry.containsUser(playerID)) throw new RestrictionException("Unknown user");
		return matchesManager.getMatchByDate(playerID, leagueID, startDate, endDate, index);
	}
	
	/**
	 * System facade for <code>MatchesManager.getMatchDate</code>
	 * 
	 * @see MatchesManager.getMatchDate
	 */
	public String getMatchDate(int matchID) {
		return getDateFormat().format(matchesManager.getMatchDate(matchID));
	}
	
	/**
	 * System facade for <code>MatchesManager.getMatchWinner</code>
	 * @throws RestrictionException 
	 * 
	 * @see MatchesManager.getMatchWinner
	 */
	public String getMatchWinner(String matchID) throws RestrictionException {
		
		int parsedMatchID = Integer.MIN_VALUE;
		
		try {
			parsedMatchID = Integer.parseInt(matchID);
		}catch (NumberFormatException e) {
			throw new RestrictionException("Unknown match");
		}
		return matchesManager.getMatchWinner(parsedMatchID);
	}
	
	/**
	 * System facade for <code>MatchesManager.getMatchLoser</code>
	 * @throws RestrictionException 
	 * 
	 * @see MatchesManager.getMatchLoser
	 */
	public String getMatchLoser(String matchID) throws RestrictionException {
		
		int parsedMatchID = Integer.MIN_VALUE;
		
		try {
			parsedMatchID = Integer.parseInt(matchID);
		}catch (NumberFormatException e) {
			throw new RestrictionException("Unknown match");
		}
		return matchesManager.getMatchLoser(parsedMatchID);
	}
	
	/**
	 * System facade for <code>MatchesManager.getMatchLength</code>
	 * 
	 * @see MatchesManager.getMatchLength
	 */
	public String getMatchLength(int matchID) {
		return matchesManager.getMatchLength(matchID);
	}
	
	/**
	 * System facade for <code>MatchesManager.getMatchScore</code>
	 * 
	 * @see MatchesManager.getMatchScore
	 */
	public String getMatchScore(int matchID) {
		return matchesManager.getMatchScore(matchID);
	}
	
	/**
	 * System facade for <code>MatchesManager.getMatchLongestRunForWinner</code>
	 * 
	 * @see MatchesManager.getMatchLongestRunForWinner
	 */
	public String getMatchLongestRunForWinner(int matchID) {
		return matchesManager.getMatchLongestRunForWinner(matchID);
	}
	
	/**
	 * System facade for <code>MatchesManager.getMatchLongestRunForLoser</code>
	 * 
	 * @see MatchesManager.getMatchLongestRunForLoser
	 */
	public String getMatchLongestRunForLoser(int matchID) {
		return matchesManager.getMatchLongestRunForLoser(matchID);
	}

	/**
	 * System facade for <code>MatchesManager.removeAllMatches</code>
	 * 
	 * @see MatchesManager.removeAllMatches
	 */
	public void removeAllMatches() {
		matchesManager.removeAllMatches();
	}
	
	/**
	 * System facade for <code>MatchesManager.deleteMatch</code>
	 * 
	 * @see MatchesManager.deleteMatch
	 */
	public void deleteMatch(int matchID) {
		matchesManager.deleteMatch(matchID);
	}

	/**
	 * @param format
	 */
	public void setDateFormat(SimpleDateFormat format) {
		this.dateFormat = format;
	}
	
	/**
	 * @return
	 */
	public DateFormat getDateFormat() {
		return dateFormat;
	}
	
}